package cn.edu.hitsz.compiler.lexer;

import cn.edu.hitsz.compiler.symtab.SymbolTable;
import cn.edu.hitsz.compiler.utils.FileUtils;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.StreamSupport;

/**
 * TODO: 实验一: 实现词法分析
 * <br>
 * 你可能需要参考的框架代码如下:
 *
 * @see Token 词法单元的实现
 * @see TokenKind 词法单元类型的实现
 */
public class LexicalAnalyzer {
    private final SymbolTable symbolTable;
    private StringBuilder srcCode;
    private final List<Token> token_list = new ArrayList<>();

    private int checking_pos;

    public LexicalAnalyzer(SymbolTable symbolTable) {
        this.symbolTable = symbolTable;
    }


    /**
     * 从给予的路径中读取并加载文件内容
     * 存至缓冲区srcCode中
     *
     * @param path 路径
     */
    public void loadFile(String path) {
        // TODO: 词法分析前的缓冲区实现
        srcCode = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            String line;
            while ((line = br.readLine()) != null) {
                srcCode.append(line).append("\n");
            }
        } catch (IOException e) {
            // 处理异常
            System.out.println("load file error!");
            e.printStackTrace();
        }
        System.out.println("load file success!\n");
    }


    /**
     * 执行词法分析, 准备好用于返回的 token 列表 <br>
     * 需要维护实验一所需的符号表条目, 而得在语法分析中才能确定的符号表条目的成员可以先设置为 null
     */
    public void run() {
        // TODO: 自动机实现的词法分析过程
        checking_pos = 0;
        while (checking_pos < srcCode.length()) {
            char x = srcCode.charAt(checking_pos);
            // 处理空格或转行符
            if (x == ' ' || x == '\n') {
                checking_pos++;
                continue;
            }
            Token add_token = switch (getCharType(x)) {
                case LETTER -> processLetter();
                case DIGIT -> processDigit();
                case SEMICOLON -> processSemicolon();
                case OPERATOR -> processOperator(x);
                default -> throw new IllegalStateException("Unexpected character: " + x);
            };
            token_list.add(add_token);
        }
        token_list.add(Token.eof());
    }

    // 辅助函数：返回字符的类型
    private CharType getCharType(char x) {
        if (Character.isLetter(x)) {
            return CharType.LETTER;
        } else if (Character.isDigit(x)) {
            return CharType.DIGIT;
        } else if (x == ';') {
            return CharType.SEMICOLON;
        } else if (isOperator(x)) {
            return CharType.OPERATOR;
        }
        return CharType.UNKNOWN;
    }

    // 定义字符类型
    enum CharType {
        LETTER, DIGIT, SEMICOLON, OPERATOR, UNKNOWN
    }

    private Token processLetter() {
        int start = checking_pos;
        while (checking_pos < srcCode.length() &&
                Character.isLetterOrDigit(srcCode.charAt(checking_pos))) {
            checking_pos++;
        }
        int end = checking_pos;

        // 单词
        String word = srcCode.substring(start, end);
        System.out.println("processing word:" + srcCode.substring(start, end));

        Token token;
        if (TokenKind.isAllowed(word))  //关键字
        {
            token = Token.simple(word);
        } else   //标识符
        {
            token = Token.normal("id", word);
            if (!symbolTable.has(word))
                symbolTable.add(word);
        }
        return token;
    }

    private Token processDigit() {
        int start = checking_pos;
        while (Character.isDigit(srcCode.charAt(checking_pos)))
            checking_pos++;
        int end = checking_pos;

        // 词法单元
        String word = srcCode.substring(start, end);
        System.out.println("processing digits:" + srcCode.substring(start, end));

        return Token.normal("IntConst", word);
    }

    private Token processSemicolon() {
        checking_pos++;
        return Token.simple("Semicolon");
    }

    private boolean isOperator(char x) {
        return "+-*/=()".indexOf(x) >= 0;
    }

    private Token processOperator(char x) {
        checking_pos++;
        return Token.simple(Character.toString(x));
    }

    /**
     * 获得词法分析的结果, 保证在调用了 run 方法之后调用
     *
     * @return Token 列表
     */
    public Iterable<Token> getTokens() {
        // TODO: 从词法分析过程中获取 Token 列表
        // 词法分析过程可以使用 Stream 或 Iterator 实现按需分析
        // 亦可以直接分析完整个文件
        // 总之实现过程能转化为一列表即可
        return token_list;
    }

    /**
     * 将token写入文件
     *
     * @param path 文件路径
     */
    public void dumpTokens(String path) {
        FileUtils.writeLines(
                path,
                StreamSupport.stream(getTokens().spliterator(), false).map(Token::toString).toList()
        );
    }
}
